﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using MiniCPU.Entities;

namespace MiniCPU.SimpleCPU
{
    /// <summary>
    /// Control Unit Entity.
    /// </summary>
    public class ControlUnit
    {
        #region Variables
        /// <summary>
        /// Control Signals collection.
        /// </summary>
        private Dictionary<string, DataPropagator> controlSignals;
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="ControlUnit"/> class.
        /// </summary>
        /// <param name="sequenceCounter">The sequence counter.</param>
        /// <param name="instructionRegister">The Instruction register (IR).</param>
        public ControlUnit(Register sequenceCounter, Register instructionRegister)
        {
            this.SequenceCounter = sequenceCounter;
            this.IR = instructionRegister;

            this.controlSignals = new Dictionary<string, DataPropagator>();

            this.SequenceCounter.DataChanged += new MiniCPU.Events.DataChangedEventHandler(this.RefreshControlSignals);
            this.IR.DataChanged += new MiniCPU.Events.DataChangedEventHandler(this.RefreshControlSignals);

            this.CreateControlSignals();
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the sequence counter.
        /// </summary>
        /// <value>The sequence counter.</value>
        public LimitedData SequenceCounter { get; set; }

        /// <summary>
        /// Gets or sets the IR.
        /// </summary>
        /// <value>The Instruction Register (IR).</value>
        public LimitedData IR { get; set; }

        /// <summary>
        /// Gets the <see cref="MiniCPU.Entities.LimitedData"/> with the specified control signal name.
        /// </summary>
        /// <value>LimitedData representing the control signal.</value>
        public LimitedData this[string controlSignalName]
        {
            get
            {
                return this.controlSignals[controlSignalName];
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Creates the control signals.
        /// </summary>
        private void CreateControlSignals()
        {
            this.controlSignals.Add("T0", new DataPropagator("T0", false));
            this.controlSignals.Add("T1", new DataPropagator("T1", false));
            this.controlSignals.Add("T2", new DataPropagator("T2", false));
            this.controlSignals.Add("D0T3", new DataPropagator("D0T3", false));
            this.controlSignals.Add("D0T4", new DataPropagator("D0T4", false));
            this.controlSignals.Add("D1T3", new DataPropagator("D1T3", false));
            this.controlSignals.Add("D1T4", new DataPropagator("D1T4", false));
            this.controlSignals.Add("D2T3", new DataPropagator("D2T3", false));
            this.controlSignals.Add("IR0D3T3", new DataPropagator("IR0D3T3", false));
            this.controlSignals.Add("IR1D3T3", new DataPropagator("IR1D3T3", false));
        }

        /// <summary>
        /// Refreshes the control signals.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="MiniCPU.Events.DataChangedEventArgs"/> instance containing the event data.</param>
        private void RefreshControlSignals(object sender, MiniCPU.Events.DataChangedEventArgs e)
        {
            this.controlSignals["T0"].BooleanValue = this.SequenceCounter.IntValue == 0;
            Debug.Assert(true, "T0 signal is now " + this.controlSignals["T0"].BooleanValue.ToString());

            this.controlSignals["T1"].BooleanValue = this.SequenceCounter.IntValue == 1;
            Debug.Assert(true, "T1 signal is now " + this.controlSignals["T1"].BooleanValue.ToString());

            this.controlSignals["T2"].BooleanValue = this.SequenceCounter.IntValue == 2;
            Debug.Assert(true, "T2 signal is now " + this.controlSignals["T2"].BooleanValue.ToString());

            this.controlSignals["D0T3"].BooleanValue = this.SequenceCounter.IntValue == 3 && this.IR.GetPartOfData(7, 6).IntValue == 0;
            Debug.Assert(true, "D0T3 signal is now " + this.controlSignals["D0T3"].BooleanValue.ToString());

            this.controlSignals["D0T4"].BooleanValue = this.SequenceCounter.IntValue == 4 && this.IR.GetPartOfData(7, 6).IntValue == 0;
            Debug.Assert(true, "D0T4 signal is now " + this.controlSignals["D0T4"].BooleanValue.ToString());

            this.controlSignals["D1T3"].BooleanValue = this.SequenceCounter.IntValue == 3 && this.IR.GetPartOfData(7, 6).IntValue == 1;
            Debug.Assert(true, "D1T3 signal is now " + this.controlSignals["D1T3"].BooleanValue.ToString());

            this.controlSignals["D1T4"].BooleanValue = this.SequenceCounter.IntValue == 4 && this.IR.GetPartOfData(7, 6).IntValue == 1;
            Debug.Assert(true, "D1T4 signal is now " + this.controlSignals["D1T4"].BooleanValue.ToString());

            this.controlSignals["D2T3"].BooleanValue = this.SequenceCounter.IntValue == 3 && this.IR.GetPartOfData(7, 6).IntValue == 2;
            Debug.Assert(true, "D2T3 signal is now " + this.controlSignals["D2T3"].BooleanValue.ToString());

            this.controlSignals["IR0D3T3"].BooleanValue = this.SequenceCounter.IntValue == 3 && this.IR.GetPartOfData(7, 6).IntValue == 3 && this.IR.GetPartOfData(0, 0).BooleanValue;
            Debug.Assert(true, "IR0D3T3 signal is now " + this.controlSignals["IR0D3T3"].BooleanValue.ToString());

            this.controlSignals["IR1D3T3"].BooleanValue = this.SequenceCounter.IntValue == 3 && this.IR.GetPartOfData(7, 6).IntValue == 3 && this.IR.GetPartOfData(1, 1).BooleanValue;
            Debug.Assert(true, "IR1D3T3 signal is now " + this.controlSignals["IR1D3T3"].BooleanValue.ToString());
        }
        #endregion
    }
}
